/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package modelo.primitivas;

import modelo.primitivas.Punto;
import com.vividsolutions.jts.geom.LineSegment;
import com.vividsolutions.jts.geom.Coordinate;
import java.util.*;

/**
 *
 * @author Diego
 */
public class SegmentoLinea {

    private Punto origen;
    private Punto fin;
    private LineSegment segmento;
    private String etiqueta = "";

    public SegmentoLinea(Punto pOrigen,Punto pFin)
    {
        origen = pOrigen;
        fin = pFin;
        segmento = new LineSegment(pOrigen.getPuntoCoordenada(), pFin.getPuntoCoordenada());
    }

    public Punto getOrigen()
    {
        return origen;
    }

    public Punto getFin()
    {
        return fin;
    }

    public void setOrigen(Punto pOrigen)
    {
        origen = pOrigen;
        segmento.p0 = pOrigen.getPuntoCoordenada();
    }

    public void setFin(Punto pFin)
    {
        fin = pFin;
        segmento.p1 = pFin.getPuntoCoordenada();
    }

    public String getEtiqueta()
    {
        return etiqueta;
    }

     public void setEtiqueta(String s)
    {
        etiqueta = s;
    }

    /**
     * Evalúa si 2 segmentos de recta se intersectan y en donde
     * @param vSegmento Segmento de comparación
     * @return null si no hay intersección o el punto donde se intersecta
     */
    public Punto interseccionSegmentoLinea(SegmentoLinea vSegmento)
    {
        Coordinate coor = segmento.intersection(vSegmento.segmento);
        if(coor == null)
            return null;
        else return new Punto(coor);
    }

    /**
     * Busca un punto perteneciente al segmento, más cercano a un punto dado.
     * Si el punto hace parte del segmento retorna el mismo punto.
     * @param comparado Punto de comparación
     * @return El punto más cercano
     */
    public Punto puntoCercano(Punto comparado)
    {
        return new Punto(segmento.closestPoint(comparado.getPuntoCoordenada()));
    }

    /**
     * Retorna la distancia de un punto a un segmento de recta.  Si la distancia es cero
     * el punto hace parte del segmento.
     * @param comparado Punto de comparación
     * @return distancia
     */
    public double distanciaPunto(Punto comparado)
    {
        return segmento.distance(comparado.getPuntoCoordenada());
    }

    public boolean esPuntoFinal(Punto comparado)
    {
        if(comparado.esIgual(origen) || comparado.esIgual(fin))
            return true;
        else return false;
    }
    
    public Set<Punto> puntosIncidentesNoFinales(Set<Punto> pPuntosBusqueda)
    {
        try
        {
            Set<Punto> conjuntoResultante = new HashSet<Punto>();
            Iterator<Punto> recorrido1 = pPuntosBusqueda.iterator();

            while(recorrido1.hasNext())
            {
                Punto puntoBusqueda = recorrido1.next();
                if(!esPuntoFinal(puntoBusqueda))
                {
                    double vDistancia = distanciaPunto(puntoBusqueda);
                    if (vDistancia==0)
                    {
                        conjuntoResultante.add(puntoBusqueda);
                    }
                }
            }
            return conjuntoResultante;
        }
        catch(Exception ex)
        {
            System.err.println(ex);
            return null;
        }
        
    }

    public Set<Punto> puntosInterseccion(Set<SegmentoLinea> conjuntoSegmentos)
    {
        try
        {
            Set<Punto> conjuntoSalida = new HashSet<Punto>();
            Iterator<SegmentoLinea> recorrido = conjuntoSegmentos.iterator();

            while(recorrido.hasNext())
            {
                SegmentoLinea segmentoBusqueda = recorrido.next();
                if(!this.esSimilar(segmentoBusqueda))
                {
                    Punto pInterseccion = interseccionSegmentoLinea(segmentoBusqueda);
                    if (pInterseccion!= null)
                    {
                        if(!segmentoBusqueda.esPuntoFinal(pInterseccion))
                        {
                            if(!this.esPuntoFinal(pInterseccion))
                            {
                                if(!Punto.existeEnConjuntoPuntos(conjuntoSalida, pInterseccion))
                                    conjuntoSalida.add(pInterseccion);
                            }
                        }
                    }
                }
            }
            return conjuntoSalida;
        }
        catch(Exception ex)
        {
            System.err.println(ex);
            return null;
        }
    }

    public SegmentoLinea seleccionarSegmentoSiguiente(Set<SegmentoLinea> conjunto,List<SegmentoLinea> listado)
    {
        SegmentoLinea nuevo = null;
        double angulo = 2*Math.PI;
        
        if(conjunto!= null)
        {
            Iterator<SegmentoLinea> siguiente = conjunto.iterator();
            while(siguiente.hasNext())
            {
                SegmentoLinea vSegmento = siguiente.next();
                if(!SegmentoLinea.existeEnListaSegmentos(vSegmento, listado))
                {
                    double anguloInterno = anguloInterno(vSegmento);
                    if(anguloInterno<angulo)
                    {
                        angulo = anguloInterno;
                        nuevo = vSegmento;
                    }
                }
            }
                
        }
        return nuevo;
    }

    public boolean esIgual(SegmentoLinea pSegmento)
    {
        if(origen.esIgual(pSegmento.origen)&&fin.esIgual(pSegmento.fin))
            return true;
        else return false;
    }

    public boolean esSimilar(SegmentoLinea pSegmento)
    {
        if(origen.esIgual(pSegmento.origen)&&fin.esIgual(pSegmento.fin))
            return true;
        else if(origen.esIgual(pSegmento.fin)&&fin.esIgual(pSegmento.origen))
            return true;
        else return false;
    }
    
    public boolean esUnPunto()
    {
        if(origen.esIgual(fin))
            return true;
        else return false;
    }

    public void imprimirSegmentoRecta()
    {
        System.out.print("Origen: ");
        origen.imprimirPunto();
        System.out.print("- Fin: ");
        fin.imprimirPunto();
        System.out.println();
    }

    public String textoSegmentoRecta()
    {
        StringBuilder texto = new StringBuilder();
        texto.append("Origen: ");
        texto.append(origen.textoPunto());
        texto.append("- Fin: ");
        texto.append(fin.textoPunto());
        texto.append("\n");
        return texto.toString();
    }

    public static void imprimirConjuntoSegmentos(Set<SegmentoLinea> pConjunto,String titulo)
    {
        System.out.println(titulo);
        Iterator<SegmentoLinea> recorrido = pConjunto.iterator();
        while(recorrido.hasNext())
        {
            recorrido.next().imprimirSegmentoRecta();
        }
    }

    public static void imprimirListadoSegmentos(List<SegmentoLinea> pConjunto,String titulo)
    {
        System.out.println(titulo);
        Iterator<SegmentoLinea> recorrido = pConjunto.iterator();
        while(recorrido.hasNext())
        {
            recorrido.next().imprimirSegmentoRecta();
        }
    }

    public static String textoConjuntoSegmentos(Set<SegmentoLinea> pConjunto,String titulo)
    {
        StringBuilder texto = new StringBuilder();
        texto.append(titulo+"\n");
        Iterator<SegmentoLinea> recorrido = pConjunto.iterator();
        while(recorrido.hasNext())
        {
            texto.append(recorrido.next().textoSegmentoRecta());
        }
        return texto.toString();
    }

    public static String textoListadoSegmentos(List<SegmentoLinea> pConjunto,String titulo)
    {
        StringBuilder texto = new StringBuilder();
        texto.append(titulo+"\n");
        Iterator<SegmentoLinea> recorrido = pConjunto.iterator();
        while(recorrido.hasNext())
        {
            texto.append(recorrido.next().textoSegmentoRecta());
        }
        return texto.toString();
    }

    public static boolean existeEnListaSegmentos(SegmentoLinea pSegmento,List<SegmentoLinea> listaSegmentos)
    {
        ListIterator<SegmentoLinea> recorrido = listaSegmentos.listIterator();
        while(recorrido.hasNext())
        {
            SegmentoLinea comprobar = recorrido.next();
            if(comprobar.esSimilar(pSegmento))
            {
                return true;
            }
        }
        return false;
    }

    public static Set<SegmentoLinea> quitarTodosPelos(Set<SegmentoLinea> pConjunto)
    {
        Set<SegmentoLinea> conjuntoSinPelos = quitarPelos(pConjunto);
        if(conjuntoSinPelos!=null)
        {
            if(conjuntoSinPelos.size()>2)
            {
                if(pConjunto.size()>conjuntoSinPelos.size())
                    conjuntoSinPelos = quitarTodosPelos(conjuntoSinPelos);
            }
            else conjuntoSinPelos=new HashSet<SegmentoLinea>();

        }
        return conjuntoSinPelos;
    }

    public static Set<SegmentoLinea> quitarPelos(Set<SegmentoLinea> pConjunto)
    {
        Set<SegmentoLinea> conjuntoSinPelos = new HashSet<SegmentoLinea>();
        Iterator<SegmentoLinea> recorrido1 = pConjunto.iterator();
        while(recorrido1.hasNext())
        {
            boolean pOrigen = false;
            boolean pFin = false;
            SegmentoLinea busqueda1 = recorrido1.next();
            Iterator<SegmentoLinea> recorrido2 = pConjunto.iterator();
            while(recorrido2.hasNext())
            {
                SegmentoLinea busqueda2 = recorrido2.next();
                if(!busqueda1.esSimilar(busqueda2))
                {
                    if(busqueda2.esPuntoFinal(busqueda1.origen))
                    {
                        pOrigen = true;
                    }
                    else if(busqueda2.esPuntoFinal(busqueda1.fin))
                    {
                        pFin = true;
                    }
                }
            }
            if(pOrigen&&pFin)
                conjuntoSinPelos.add(busqueda1);

        }
        return conjuntoSinPelos;
    }
    
    public double getSaltosEnX(double espacioGrillaX)
    {
        return Math.abs(origen.getCoordenadaX()-fin.getCoordenadaX())/espacioGrillaX;
    }

    public SegmentoLinea inverso()
    {
        return new SegmentoLinea(fin,origen);
    }
    
    public double getSaltosEnY(double espacioGrillaY)
    {
        return Math.abs(origen.getCoordenadaY()-fin.getCoordenadaY())/espacioGrillaY;
    }

    public double anguloEjeX()
    {
        double angulo = segmento.angle();
        if(angulo<0)
            angulo = (2*Math.PI)+angulo;
        return angulo;
    }

    public double anguloInterno(SegmentoLinea pSegmento)
    {
        double anguloX = 0;
        double anguloPX = 0;
        double anguloInterno = 0;
        if(origen.esIgual(pSegmento.getOrigen()))
        {
            anguloX = anguloEjeX();
            anguloPX = pSegmento.anguloEjeX();
        }
        else if(origen.esIgual(pSegmento.getFin()))
        {
            anguloX = anguloEjeX();
            anguloPX = pSegmento.inverso().anguloEjeX();
        }
        else if(fin.esIgual(pSegmento.getOrigen()))
        {
            anguloX = inverso().anguloEjeX();
            anguloPX = pSegmento.anguloEjeX();
        }
        else
        {
            anguloX = inverso().anguloEjeX();
            anguloPX = pSegmento.inverso().anguloEjeX();
        }
        if(anguloX>anguloPX)
            anguloInterno=(2*Math.PI-anguloX)+anguloPX;
        else anguloInterno=anguloPX-anguloX;
        return anguloInterno;

    }
    
    public static Set<SegmentoLinea> segmentosPuntoFinal(Set<SegmentoLinea> conjuntoSeg,SegmentoLinea sLinea,Punto inicio)
    {
        Set<SegmentoLinea> conjunto = new HashSet<SegmentoLinea>();
        Iterator<SegmentoLinea> recorrido = conjuntoSeg.iterator();
        while(recorrido.hasNext())
        {
            SegmentoLinea nuevo = recorrido.next();
            if(!nuevo.esSimilar(sLinea))
            {
                if(nuevo.esPuntoFinal(inicio))
                    conjunto.add(nuevo);
            }
        }
        return conjunto;
    }
    
    public static Set<Punto> toConjuntoPuntos(Set<SegmentoLinea> segmentos)
    {
        Iterator<SegmentoLinea> recorrido = segmentos.iterator();
        Set<Punto> puntos = new HashSet<Punto>();
        while(recorrido.hasNext())
        {
            SegmentoLinea nueva = recorrido.next();
            if(!Punto.existeEnConjuntoPuntos(puntos,nueva.getOrigen()))
                puntos.add(nueva.getOrigen());
            if(!Punto.existeEnConjuntoPuntos(puntos,nueva.getFin()))
                puntos.add(nueva.getFin());
        }
        return puntos;
    }

    public static SegmentoLinea puntoIncidente(Set<SegmentoLinea> pSegmentos, Punto pPunto)
    {
        Iterator<SegmentoLinea> recorrido = pSegmentos.iterator();
        while(recorrido.hasNext())
        {
            SegmentoLinea nuevo = recorrido.next();
            if(nuevo.esPuntoFinal(pPunto))
                return nuevo;
        }
        return null;
    }
}
